package timingwheel

import (
	"container/list"
	"sync"
	"sync/atomic"
	"time"
)

// SingleTimingWheel 定义单级时间轮的数据结构
type SingleTimingWheel struct {
	// slots 槽数组，每个槽是一个链表，用于连接多个定时任务
	slots []*list.List
	// slotNumber 槽的数量
	slotNumber int
	// tickDuration 每个槽的时间间隔
	tickDuration time.Duration
	// currentPosition 当前指针位置
	currentPosition int
	// taskChan 任务队列
	taskChan chan *Task
	// stopChan 停止信号通道
	stopChan chan struct{}
	// 原子标记是否已停止
	stopped atomic.Bool

	waitGroup sync.WaitGroup
	lock      sync.Mutex
}

// NewSingleTimingWheel 创建一个新的单级时间轮
func NewSingleTimingWheel(tickDuration time.Duration, slotNumber int) *SingleTimingWheel {
	// 创建初始的单级时间轮
	tw := &SingleTimingWheel{
		tickDuration: tickDuration,
		slotNumber:   slotNumber,
		slots:        make([]*list.List, slotNumber),
		taskChan:     make(chan *Task, 1024),
		stopChan:     make(chan struct{}),
	}

	// 初始化每个槽的链表
	for i := 0; i < slotNumber; i++ {
		tw.slots[i] = list.New()
	}

	return tw
}

// Start 启动时间轮
func (own *SingleTimingWheel) Start() {
	own.waitGroup.Add(1)
	go own.run()
}

// AddTask 添加一个定时任务
func (own *SingleTimingWheel) AddTask(delay time.Time, callback func(), repeating bool, repeats int, interval time.Duration) {
	// 若 repeats=0，相当于 repeating=false，即不重复执行
	if repeats == 0 {
		repeating = false
		interval = 0
	}

	// 添加任务到任务队列
	own.taskChan <- &Task{
		delay:       delay.UnixNano(),
		callback:    callback,
		interval:    interval,
		repeatTimes: repeats,
		repeating:   repeating,
	}
}

// Stop 停止时间轮
func (own *SingleTimingWheel) Stop() {
	close(own.stopChan)
	own.waitGroup.Wait()
}

// run 运行时间轮
func (own *SingleTimingWheel) run() {
	defer own.waitGroup.Done()

	// 创建一个计时器
	ticker := time.NewTicker(own.tickDuration)
	defer ticker.Stop()

	// 循环处理
	for {
		select {
		case <-ticker.C: // 触发时间轮转动，执行下一个槽里的任务
			own.deal()
		case task := <-own.taskChan: // 添加任务到时间轮
			own.addTaskToSlot(task)
		case <-own.stopChan: // 停止时间轮运作
			return
		}
	}
}

// deal 处理一个时间滴答
func (own *SingleTimingWheel) deal() {
	own.lock.Lock()

	// 获取当前槽的任务列表
	currentSlot := own.slots[own.currentPosition]

	// 处理当前槽中的所有任务
	now := time.Now().UnixNano()
	for e := currentSlot.Front(); e != nil; {
		task := e.Value.(*Task)

		// 任务时间到，执行回调
		if task.delay-now <= int64(own.tickDuration) {
			own.callback(task)

			// 如果是重复任务，重新计算到期时间并添加回时间轮
			if task.repeating && task.repeatTimes != task.callbackTimes {
				task.delay = now + int64(task.interval)
				own.addTaskToSlot(task)
			}

			// 从当前槽中移除任务
			currentSlot.Remove(e)
		}

		e = e.Next()
	}

	// 移动指针到下一个槽
	own.currentPosition = (own.currentPosition + 1) % own.slotNumber
	own.lock.Unlock()
}

// addTaskToSlot 将任务添加到合适的槽中
func (own *SingleTimingWheel) addTaskToSlot(task *Task) {
	now := time.Now().UnixNano()

	// 任务已经过期，立即执行
	if task.delay-now <= int64(own.tickDuration) {
		own.callback(task)
	}

	// 检查是否是否还需要执行
	if !task.repeating && task.repeatTimes != task.callbackTimes {
		return
	}

	// 计算任务应该在多少个时间滴答后执行
	ticks := (task.delay - now) / int64(own.tickDuration)
	// 计算任务应该放入的槽位置
	pos := (own.currentPosition + int(ticks%int64(own.slotNumber))) % own.slotNumber
	// 将任务添加到对应的槽中
	own.slots[pos].PushBack(task)
}

func (own *SingleTimingWheel) callback(task *Task) {
	go func() {
		own.waitGroup.Add(1)
		task.callback()
		task.callbackTimes++
		own.waitGroup.Done()
	}()
}
